/* 最大点权独立集
* 1.独立集 
    选择的点集之间两两之间不存在边

* 2.最大权独立集
    选择的独立集的权值之和最大

* 3.独立集和点覆盖集是对偶的关系
    对于一个集合 V′，如果 V′是独立集，则 V−V′ 一定是点覆盖集，如果 V−V′ 不是点覆盖集，
    即存在一条边 x−y，x 和 y 都不在 V−V′ 里面，则 x 和 y 一定在 V′ 里面，由于 x 和 y 之间有边，与 V′ 是独立集矛盾
    如果V′是独立集，则V−V′一定是点覆盖集
    同理: 如果V′是独立集，则V−V′一定是点覆盖集

* 4.最大权独立集=点权值之和−最小权点覆盖


* 本题:
    建二分图，即选择最大独立集，相邻之间就没有边，即不能拿走相邻宝石
*/
#define DEBUG
#pragma GCC optimize("O1,O2,O3,Ofast")
#pragma GCC optimize("no-stack-protector,unroll-loops,fast-math,inline")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse,sse2,sse3,sse4,sse4.1,sse4.2,ssse3")

#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010, M = 60010, INF = 2147400000;
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = { 0, 1, 0,-1};

int n, m, S, T;
int h[N], ne[M], c[M], e[M], idx;
int d[N], q[N], cur[N];

void AddEdge(int a, int b, int w)
{
    e[idx] = b, c[idx] = w, ne[idx] = h[a], h[a] = idx++;
    e[idx] = a, c[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}

inline int get(int x, int y) //二维坐标->一维坐标
{
    return (x-1)*m+y;
}

bool bfs()
{
    int hh = 0, tt = -1;
    memset(d, -1, sizeof d);
    q[++tt] = S, cur[S] = h[S], d[S] = 0;
    while(hh <= tt)
    {
        int u = q[hh++];
        for(int i = h[u]; ~i; i = ne[i])
        {
            int v = e[i];
            if(d[v] == -1 && c[i])
            {
                d[v] = d[u] + 1;
                cur[v] = h[v];
                if(v == T) return true;
                q[++tt] = v;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if(u == T) return limit;
    int flow = 0;
    for(int i = cur[u]; ~i && limit > flow; cur[u] = i, i = ne[i])
    {
        int v = e[i];
        if(d[v] == d[u] + 1 && c[i])
        {
            int t = find(v, min(c[i], limit - flow));
            if(!t) d[v] = -1;
            c[i] -= t, c[i^1] += t, flow += t;
        }
    }
    return flow;
}

int Dinic() 
{
    int res = 0, flow = 0;
    while(bfs())
        while((flow = find(S, INF))) /*printf("res:%d ", flow),*/ res += flow;
    return res; 
}

signed main()
{
    #ifdef DEBUG
        freopen("./in.txt", "r", stdin);
    #else
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    #endif

    memset(h, -1, sizeof h);
    cin >> n >> m;
    
    S = 0, T = m*n+1;
    int tot = 0;

    for(int x=1; x<=n; x++) 
        for(int y=1; y<=m; y++)
        {    
            int w; cin >> w; tot += w; //w:w[x][y]
            //printf("[%d, %d] %d tot:%d\n", x, y, w[x][y], tot);
            if((x+y)&1) //奇数点
            {
                AddEdge(S, get(x, y), w); //以任意奇数点为起点
                for(int k=0; k<4; k++)
                {
                    int nx = x+dx[k], ny = y+dy[k];
                    if(nx<1 || nx>n || ny<1 || ny>m) continue;

                    AddEdge(get(x, y), get(nx, ny), INF); //当前点到邻点无边
                }
            }
            else    
                AddEdge(get(x, y), T, w); //以任意偶数点为结尾
        }

    printf("%d\n", tot-Dinic());


    return 0;
}
